----------------------------------------------------------------------------------
-- CPEG 422 spartan-3e base
-- Jason Parrott
-- Rodney McGee
-- Ray Delvecchio
-- Juergen Ributzka
-- Jack Welsh
-- Ryan Hoover
-- Mike Stamat
-- Lee Reed
--
-- LCD Code
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity LCD is
  port ( clk      : in  std_logic;
         reset    : in  std_logic;
         data_in  : in  std_logic_vector (7 downto 0);
         enable   : in  std_logic;
         LCD_D    : out std_logic_vector (11 downto 8);
         LCD_E    : out std_logic;
         finished : out std_logic);
end LCD;

architecture Behavioral of LCD is

  type statetype_init is (I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13);
  type statetype_write is (W0, W1, W2, W3, W4, W5);

  signal state_init, next_state_init   : statetype_init  := I0;
  signal state_write, next_state_write : statetype_write := W0;

  signal InitComplete : std_logic := '0';

begin

  state_init  <= next_state_init;
  state_write <= next_state_write;

  LCD_Init            : process(clk, reset)
    variable count    : std_logic_vector (19 downto 0) := x"00000";
    variable dispWait : integer                        := 0;
  begin

    if (reset = '1') then
      count    := x"00000";
      InitComplete <= '0';
      finished     <= '0';
      dispWait := 0;
    elsif (rising_edge(clk)) then

      --Default is zero, but may be overwritten in the case statement
      LCD_E <= '0';

      if (InitComplete = '0') then

        case state_init is

          --Init state I0: wait 15ms (750,000 cycles @ 50 MHz)
          when I0 =>
            if (count = 750000-1) then
              count := x"00000";
              next_state_init <= I1;
            else
              count := count + 1;
              next_state_init <= I0;
            end if;

            --Init state I1-I2: write 0x3
            --I1: wait 2 cycles before asserting enable (LCD_E)
            --I2: assert LCD_E, keep high for 12 cycles
          when I1 =>
            LCD_D             <= x"3";
            if (count = 2-1) then
              count := x"00000";
              next_state_init <= I2;
            else
              count := count + 1;
              next_state_init <= I1;
            end if;

          when I2 =>
            LCD_D             <= x"3";
            LCD_E             <= '1';
            if (count = 12-1) then
              count := x"00000";
              next_state_init <= I3;
            else
              count := count + 1;
              next_state_init <= I2;
            end if;

            --Init state I3: wait 4.1ms (205,000 cycles @ 50 MHz)
          when I3 =>
            if (count = 205000-1) then
              count := x"00000";
              next_state_init <= I4;
            else
              count := count + 1;
              next_state_init <= I3;
            end if;

            --Init state I4-I5: write 0x3
            --I4: wait 2 cycles before asserting enable (LCD_E)
            --I5: assert LCD_E, keep high for 12 cycles
          when I4 =>
            LCD_D             <= x"3";
            if (count = 2-1) then
              count := x"00000";
              next_state_init <= I5;
            else
              count := count + 1;
              next_state_init <= I4;
            end if;

          when I5 =>
            LCD_D             <= x"3";
            LCD_E             <= '1';
            if (count = 12-1) then
              count := x"00000";
              next_state_init <= I6;
            else
              count := count + 1;
              next_state_init <= I5;
            end if;

            --Init state I6: wait 100us (5,000 cycles @ 50 MHz)
          when I6 =>
            if (count = 5000-1) then
              count := x"00000";
              next_state_init <= I7;
            else
              count := count + 1;
              next_state_init <= I6;
            end if;

            --Init state I7-I8: write 0x3
            --I7: wait 2 cycles before asserting enable (LCD_E)
            --I8: assert LCD_E, keep high for 12 cycles
          when I7 =>
            LCD_D             <= x"3";
            if (count = 2-1) then
              count := x"00000";
              next_state_init <= I8;
            else
              count := count + 1;
              next_state_init <= I7;
            end if;

          when I8 =>
            LCD_D             <= x"3";
            LCD_E             <= '1';
            if (count = 12-1) then
              count := x"00000";
              next_state_init <= I9;
            else
              count := count + 1;
              next_state_init <= I8;
            end if;

            --Init state I9: wait 40us (2,000 cycles @ 50 MHz)
          when I9 =>
            if (count = 2000-1) then
              count := x"00000";
              next_state_init <= I10;
            else
              count := count + 1;
              next_state_init <= I9;
            end if;

            --Init state I10-I11: write 0x2
            --I10: wait 2 cycles before asserting enable (LCD_E)
            --I11: assert LCD_E, keep high for 12 cycles
          when I10 =>
            LCD_D             <= x"2";
            if (count = 2-1) then
              count := x"00000";
              next_state_init <= I11;
            else
              count := count + 1;
              next_state_init <= I10;
            end if;

          when I11 =>
            LCD_D             <= x"2";
            LCD_E             <= '1';
            if (count = 12-1) then
              count := x"00000";
              next_state_init <= I12;
            else
              count := count + 1;
              next_state_init <= I11;
            end if;

            --Init state I12: wait 40us (2,000 cycles @ 50 MHz) 
          when I12 =>
            if (count = 2000-1) then
              count := x"00000";
              next_state_init <= I13;
            else
              count := count + 1;
              next_state_init <= I12;
            end if;

            --Init state I13: set flag to indicate initialization complete
            --Spin at current state
          when I13 =>
            InitComplete    <= '1';
            next_state_init <= I13;
            finished        <= '1';

          when others =>
            null;

        end case;

      elsif (InitComplete = '1') then
        LCD_E <= '0';

        -- If enable is high, write data to LCD
        if (enable = '1') then
          finished <= '0';

          case state_write is

                                        --Write state W0-W1: transmit first 4-bits of data byte
                                        --W0: wait 2 cycles before asserting enable (LCD_E)
                                        --W1: assert LCD_E, keep high for 12 cycles
                                        -- If instruction is 'Clear Display', set flag
                                        -- because this affects timing requirements
            when W0 =>
              LCD_D <= data_in(7 downto 4);

              if (data_in = x"01") then
                dispWait := 82000;
              else
                dispWait := 2000;
              end if;

              if (count = 2-1) then
                count := x"00000";
                next_state_write <= W1;
              else
                count := count + 1;
                next_state_write <= W0;
              end if;

            when W1 =>
              LCD_E              <= '1';
              LCD_D              <= data_in(7 downto 4);
              if (count = 12-1) then
                count := x"00000";
                next_state_write <= W2;
              else
                count := count + 1;
                next_state_write <= W1;
              end if;

                                        --Write state W2-W3: transmit second 4-bits of data byte
                                        --W2: wait 2 cycles before asserting enable (LCD_E)
                                        --W3: assert LCD_E, keep high for 12 cycles
            when W2 =>
              LCD_D              <= data_in(3 downto 0);
              if (count = 50-1) then
                count := x"00000";
                next_state_write <= W3;
              else
                count := count + 1;
                next_state_write <= W2;
              end if;

            when W3 =>
              LCD_E              <= '1';
              LCD_D              <= data_in(3 downto 0);
              if (count = 12-1) then
                count := x"00000";
                next_state_write <= W4;
              else
                count := count + 1;
                next_state_write <= W3;
              end if;

                                        --Write state W4: wait 40us (2,000 cycles @ 50 MHz) before
                                        --writing next byte of data
                                        -- If instruction was clear display, wait 82,000 cycles
            when W4 =>
              if (count = dispWait-1) then
                count := x"00000";
                next_state_write <= W5;
              else
                count := count + 1;
                next_state_write <= W4;
              end if;

                                        --Write state W5: indicate write is finished
            when W5 =>
              finished         <= '1';
              next_state_write <= W5;

            when others =>
              null;

          end case;

          --If enable is low, assert finished, and reset write state
        else
          finished         <= '1';
          next_state_write <= W0;
        end if;
      end if;
    end if;
  end process;

end Behavioral;

